Introduction
Welcome!
Code Examples
Day 1
Challenge: KISS
Solution: KISS
Day 2
Challenge: Type Annotations
Solution: Type Annotations
Day 3
Challenge: Decoupling
Solution: Decoupling
Day 4
Challenge: DRY
Solution: DRY
Day 5
Challenge: String Formatting
Solution: String Formatting
Day 6
Challenge: Law Of Demeter
Solution: Law Of Demeter
Day 7
Challenge: Better Discounts
Solution: Better Discounts
Day 8
Challenge: Payment Strategy
Solution: Payment Strategy
Day 9
Challenge: Plugins
Solution: Plugins
Day 10
Challenge: Object Oriented To Functional
Solution: Object Oriented To Functional
Day 11
Challenge: Cohesion
Solution: Cohesion
Day 12
Challenge: MVP
Solution: MVP
Day 13
Challenge: Inheritance
Solution: Inheritance
Day 14
Challenge: Abstraction
Solution: Abstraction
Day 15
Challenge: Higher-Order Functions
Solution: Higher-Order Functions
Day 16
Challenge: Configuration
Solution: Configuration
Day 17
Challenge: Concurrency
Solution: Concurrency
Day 18
Challenge: Refactoring
Solution: Refactoring
Day 19
Challenge: Itertools
Solution: Itertools
Day 20
Challenge: Inappropriate Intimacy
Solution: Inappropriate Intimacy
Wrap Up
End of Part 1

Hi all! My variant
class OrderType(StrEnum):
ONLINE = "online"
IN_STORE = "in-store"
class EmailType(StrEnum):
CONFIRMATION = auto()
SHIPPING = auto()
@dataclass
class Item:
name: str
price: Decimal
@dataclass
class Order:
id: int
type: OrderType
customer_email: str
@dataclass
class Email:
body: str
subject: str
recipient: str
sender: str
def calculate_total_price(items: Iterable[Item]) -> Decimal:
return Decimal(sum(item.price for item in items))
def calculate_discounted_price(items: Iterable[Item], discount: Decimal) -> Decimal:
return calculate_total_price(items) * (1 - discount)
def generate_email(order: Order, email_type: EmailType) -> Email:
if email_type == EmailType.CONFIRMATION:
body = f"Thank you for your order! Your order #{order.id} has been confirmed."
subject = "Order Confirmation"
elif email_type == EmailType.SHIPPING:
body = f"Good news! Your order #{order.id} has been shipped and is on its way."
subject = "Order Shipped"
else:
raise ValueError(f"Unknown email type: {email_type}")
return Email(
body=body,
subject=subject,
recipient=order.customer_email,
sender="sales@webshop.com",
)
def process_order(order: Order) -> None:
print(f"Processing {order.type} order...")
print(generate_email(order, EmailType.CONFIRMATION))
if order.type == OrderType.ONLINE:
print("Shipping the order...")
print(generate_email(order, EmailType.SHIPPING))
elif order.type == OrderType.IN_STORE:
print("Order ready for pickup.")
else:
raise ValueError(f"Unknown order type: {order.type}")
print("Order processed successfully.")
def main() -> None:
items = [
Item(name="T-Shirt", price=Decimal("19.99")),
Item(name="Jeans", price=Decimal("49.99")),
Item(name="Shoes", price=Decimal("79.99")),
]
online_order = Order(
id=123, type=OrderType.ONLINE, customer_email="sarah@gmail.com"
)
total_price = calculate_total_price(items)
print("Total price:", total_price)
discounted_price = calculate_discounted_price(items, Decimal("0.1"))
print("Discounted price:", discounted_price)
process_order(online_order)
in_store_order = Order(
id=456, type=OrderType.IN_STORE, customer_email="john@gmail.com"
)
process_order(in_store_order)
Nice solution, Arman! It looks like you are following the DRY principle. I would, however, like to give you a challenge: if you can, try to remove the if-elif-else statement from
generate_emailand see if you can separate them a bit more nicelyWatching the challenges and reading the solutions people came up with in the comments has been fascinating and inspiring. A suggestion for the site is to support markdown and provide syntax highlighting in the code block.
Hi Jacky, glad to hear you enjoy the challenges! Yes, I agree it would be great if the comment section would support Markdown and syntax highlighting. Unfortunately, it seems it's not a priority for the course platform that I use for hosting the courses as I've requested this feature already quite a while ago.
Well, I didn't quite follow directions. I did a combination between reducing repetition and changing the scope of the functions.
from dataclasses import dataclass
from decimal import Decimal
from enum import StrEnum
from typing import Iterable
class OrderType(StrEnum):
ONLINE = "online"
IN_STORE = "in store"
@dataclass
class Item:
name: str
price: Decimal
@dataclass
class Email:
body: str
subject: str
recipient: str
sender: str
@staticmethod
def generate_confirmation_email(order: "Order") -> "Email":
return Email(
body=f"Thank you for your order! Your order #{order.order_id} has been confirmed.",
subject="Order Confirmation",
recipient=order.customer_email,
sender="sales@webshop.com",
)
@staticmethod
def generate_shipping_notification(order: "Order") -> "Email":
return Email(
body=f"Good news! Your order #{order.order_id} has been shipped and is on its way.",
subject="Order Shipped",
recipient=order.customer_email,
sender="sales@webshop.com",
)
@dataclass
class Order:
order_id: int
order_type: OrderType
customer_email: str
def __init__(self, order_id: int, order_type: OrderType, customer_email: str):
self.order_id = order_id
self.order_type = order_type
self.customer_email = customer_email
def process(self) -> None:
if self.order_type == OrderType.ONLINE:
print("Processing online order...")
print(Email.generate_confirmation_email(self))
print("Shipping the order...")
print(Email.generate_shipping_notification(self))
elif self.order_type == OrderType.IN_STORE:
print("Processing in-store order...")
print(Email.generate_confirmation_email(self))
print("Order ready for pickup.")
else:
raise ValueError("Invalid order type.")
print("Order processed successfully.")
def calculate_price(items: Iterable[Item], discount: Decimal = Decimal("0")) -> Decimal:
total_price = Decimal(sum(item.price for item in items))
discounted_price = total_price - (total_price * discount)
return discounted_price
def main() -> None:
items = [
Item(name="T-Shirt", price=Decimal("19.99")),
Item(name="Jeans", price=Decimal("49.99")),
Item(name="Shoes", price=Decimal("79.99")),
]
online_order = Order(
order_id=123, order_type=OrderType.ONLINE, customer_email="sarah@gmail.com"
)
total_price = calculate_price(items, Decimal("0.0"))
print("Total price:", total_price)
discounted_price = calculate_price(items, Decimal("0.1"))
print("Discounted price:", discounted_price)
online_order.process()
in_store_order = Order(
order_id=456, order_type=OrderType.IN_STORE, customer_email="john@gmail.com"
)
in_store_order.process()
if __name__ == "__main__":
main()
Great solution! There are however one smaller remark. When using the
@dataclassdecorator, you do not need to have a__init__method. The dataclass does that for us. This does not break the code, but it is redundantI love that calculate_price() has a default discount param!
Agreed! It is a very nice addition
Why method that sending emails should know anything about order? maybe it's better to create just function with id,body, subject, recipient parameters. also we could re use it for later for example invoice confirmation.
there is a data coupling happening here. Can be argued to change for the
id,body,subjectandrecipientparameters. That would probably make the solution more future proof because of that. However, the current solution does not send the actual emails, just generating them. So, if we were to create asend_emailfunction, then we could make that more flexible.Here is my solution:
from decimal import Decimal
from dataclasses import dataclass
from enum import StrEnum
from typing import Iterable
class OrderType(StrEnum):
ONLINE = "online"
IN_STORE = "in store"
class NotificationType(StrEnum):
CONFIRMATION = "confirmation"
SHIPPING = "shipping"
@dataclass
class Item:
name: str
price: Decimal
@dataclass
class Order:
id: int
type: OrderType
customer_email: str
@dataclass
class Email:
body: str
subject: str
recipient: str
sender: str
def calculate_prices(items: Iterable[Item], discount: Decimal | None) -> dict[str, Decimal]:
list_prices: dict[str, Decimal] = {}
total_price = Decimal(sum(item.price for item in items))
list_prices["total_price"] = total_price
if discount is not None:
discounted_price = total_price - (total_price * discount)
list_prices["discounted_price"] = discounted_price
return list_prices
def generate_order_email_notifications(order: Order, notification: NotificationType) -> Email:
if notification == NotificationType.CONFIRMATION:
body = f"Thank you for your order! Your order #{order.id} has been confirmed."
subject = "Order Confirmation"
elif notification == NotificationType.SHIPPING:
body = f"Good news! Your order #{order.id} has been shipped and is on its way."
subject = "Order Shipped"
return Email(
body = body,
subject = subject,
recipient = order.customer_email,
sender = "sales@webshop.com",
)
def process_orders(order: Order) -> None:
print(f"Processing {order.type} order...")
print(generate_order_email_notifications(order, NotificationType.CONFIRMATION))
if order.type == OrderType.ONLINE:
print("Shipping the order...")
print(generate_order_email_notifications(order, NotificationType.SHIPPING))
elif order.type == OrderType.IN_STORE:
print("Order ready for pickup.")
print("Order processed successfully.")
def main() -> None:
items = [
Item(name="T-Shirt", price=Decimal("19.99")),
Item(name="Jeans", price=Decimal("49.99")),
Item(name="Shoes", price=Decimal("79.99")),
]
online_order = Order(
id=123, type=OrderType.ONLINE, customer_email="sarah@gmail.com"
)
total_price = calculate_prices(items, Decimal("0.1"))
print("Total price:", total_price["total_price"])
print("Discounted price:", total_price["discounted_price"])
process_orders(online_order)
in_store_order = Order(
id=456, type=OrderType.IN_STORE, customer_email="john@gmail.com"
)
process_orders(in_store_order)
if __name__ == "__main__":
main()
Nice solution! There are some minor remarks, like
can be changed so we have a dictionary that holds the connection between the function call and order_type. Otherwise, this is a valid solution :)